/* Simple program to pretend to be an S100 device
 * Not all commands/requests are supported yet
 * Reads in a register list from a file
 * String and integer registers are read/write, float registers return a sine waveform
 * Log dumping simply spits out all registers, all the time
 * Needs PID interface registers and logging interface registers to really emulate the system
 * Displays all communication in the GUI
 */

#include <stdlib.h>
#include <math.h>
#include <QTimer>
#include <QLabel>
#include <QTextBrowser>
#include <QFile>
#include <QDebug>
#include <qextserialport.h>
#include <registerdef.h>

#include "simulator.h"
#include "ui_simulator.h"

Simulator::Simulator(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::Simulator)
{
    ui->setupUi(this);
    ui_textBrowser = qFindChild<QTextBrowser*>(this, "textBrowser");
    ui_lblPort = qFindChild<QLabel*>(this, "lblPort");

    PortSettings targetSettings;
    targetSettings.BaudRate = BAUD115200;
    targetSettings.FlowControl = FLOW_HARDWARE;
    targetSettings.Parity = PAR_NONE;
    targetSettings.DataBits = DATA_8;
    targetSettings.StopBits = STOP_1;
    targetSettings.Timeout_Millisec = 200;

    target = new QextSerialPort(targetSettings, QextSerialPort::EventDriven);
    target->setEventChar('\x0D');
    connect(target, SIGNAL(readyRead()), this, SLOT(readPty()));

    createPty();
}

Simulator::~Simulator()
{
    if (target->isOpen())
        target->close();
    delete target;
    delete ui;
}

void Simulator::createPty()
{
#ifdef _TTY_POSIX_
    int portFd;
    target->setPortName("/dev/ptmx");
    qDebug() << "Trying to connect to" << target->portName();
    target->open(QIODevice::ReadWrite);
    portFd = target->getFd();
    if (portFd > 0) {
        grantpt(portFd);
        unlockpt(portFd);
        qDebug() << "Try connecting to" << ptsname(portFd);
    }
#endif //_TTY_POSIX_
#ifdef _TTY_WIN_
    target->setPortName("\\\\.\\COM20");
    target->open(QIODevice::ReadWrite);
#endif //_TTY_WIN_
    if (target->isOpen()) {
        target->setDtr(true);
        ui_lblPort->setText(target->portName());
        generateRegisterList();
    } else {
        qFatal("Could not connect to port.");
    }
}

void Simulator::readPty()
{
    if (target->isOpen()) {
        qint64 bytesToRead;
        QByteArray readData;
        do {
            bytesToRead = target->bytesAvailable();
            readData += target->read(bytesToRead);
            QList<QByteArray> commandList = readData.split('\x0D');
            foreach (QByteArray command, commandList) {
                if (!command.trimmed().isEmpty()) {
                    ui_textBrowser->append("r " + command.simplified());
                    writePty(parseCommand(command.trimmed()));
                }
            }
            return;
        } while (bytesToRead > 0);
    }
}

QByteArray Simulator::parseCommand(QByteArray command) {
    if (command.startsWith("X")) {
        return "root>";
    }
    if (command.startsWith("/")) {
        return "root>";
    }
    if (command.startsWith("ra")) {
        return "A";
    }
    if (command.startsWith("L")) {
        return sendRegisterList();
    }
    if (command.startsWith("R")) {
        return readRegister(command.remove(0,1));
    }
    if (command.startsWith("W")) {
        writeRegister(command.remove(0,1));
        return "A";
    }
    if (command.startsWith("D")) {
        return logDump();
        return "A";
    }
    return "A";
}

void Simulator::writePty(QByteArray reply) {
    QByteArray replyString = reply.append('\x0A').append('\x0D');
    target->write(replyString);
    if (target->waitForBytesWritten(100)) {
        ui_textBrowser->append("w " + replyString.simplified());
    } else {
        qWarning() << "Write failed in some way!";
    }
}

void Simulator::generateRegisterList() {
    RegisterDef reg;
    QFile *iFile = new QFile(":/register-list.txt");
    if (iFile->exists()) {
        iFile->open(QIODevice::ReadOnly | QIODevice::Text);
    } else {
        qFatal("No register list file found - dying.");
    }
    while (!iFile->atEnd()) {
        QString rawRegInfo = iFile->readLine(0);
        QString name = rawRegInfo.split('=').at(0);
        int flags = rawRegInfo.split('=').at(1).split(',').at(0).toInt();
        int address = rawRegInfo.split('=').at(1).split(',').at(1).toInt();
        RegisterDef reg = RegisterDef(name, address, flags);
        registerList.append(reg);
    }
    iFile->close();
    iFile->deleteLater();
}

QByteArray Simulator::sendRegisterList() {
    QByteArray reply;
    foreach (RegisterDef reg, registerList) {
        reply += "L" + reg.name().toAscii();
        reply += QString("=%1,%2").arg(reg.flags()).arg(reg.address()).toAscii();
        reply.append('\x0A').append('\x0D');
    }
    reply += "A";
    return reply;
}

QByteArray Simulator::readRegister(QByteArray address) {
    QTime curTime = QTime::currentTime();
    QString retVal;
    int regAddress = address.toInt();
    switch (registerList.at(regAddress).regtype()) {
        case REG_FLOAT:
            retVal = QString("R%1").arg(sin((double)curTime.second()), 5, 'f', 5, '0');
            break;
        case REG_INTEGER:
            retVal = "R" + registerList.at(regAddress).getValue();
            break;
        case REG_STRING:
            retVal = "R" + registerList.at(regAddress).getValue();
            break;
        default:
            retVal = "";
            break;
    }
    retVal.append('\x0A').append('\x0D').append("A");
    return retVal.toAscii();
}

void Simulator::writeRegister(QByteArray value) {
    int regAddress = value.split(' ').at(0).toInt();
    QString regValue = value.split(' ').at(1);
    registerList[regAddress].setValue(regValue);
}

QByteArray Simulator::logDump() {
    QDateTime time = QDateTime::currentDateTime();
    QByteArray reply;
    foreach (RegisterDef reg, registerList) {
        reply += "D" + reg.name().toAscii();
        reply += QString("=%1,%2").arg(reg.getValue()).arg(time.toTime_t());
        reply.append('\x0A').append('\x0D');
    }
    reply += "A";
    return reply;
}
